{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Execute this cell to install dependencies\n",
    "%pip install sf-hamilton[visualization]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Air quality analysis with numpy [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/dagworks-inc/hamilton/blob/main/examples/numpy/air-quality-analysis/hamilton_notebook.ipynb) [![GitHub badge](https://img.shields.io/badge/github-view_source-2b3137?logo=github)](https://github.com/apache/hamilton/blob/main/examples/numpy/air-quality-analysis/hamilton_notebook.ipynb)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 1 - import the things you need\n",
    "import logging\n",
    "import sys\n",
    "from functools import partial\n",
    "import typing\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "from numpy.random import default_rng\n",
    "from scipy import stats\n",
    "\n",
    "from hamilton import ad_hoc_utils, driver, base\n",
    "\n",
    "logging.basicConfig(stream=sys.stdout)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 2 - import modules to create part of the DAG from\n",
    "# We use the autoreload extension that comes with ipython to automatically reload modules when\n",
    "# the code in them changes.\n",
    "\n",
    "# import the jupyter extension\n",
    "%load_ext autoreload\n",
    "# set it to only reload the modules imported\n",
    "%autoreload 1\n",
    "# import the function modules you want to reload when they change.\n",
    "# i.e. these should be your modules you write your functions in. As you change them,\n",
    "# they will be reimported without you having to do anything.\n",
    "# %aimport analysis_flow "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Some helper functions for the calculations\n",
    "\n",
    "def _moving_mean(a, n):\n",
    "    \"\"\"Computes the moving mean using numpy constructs.\"\"\"\n",
    "    ret = np.cumsum(a, dtype=float, axis=0)\n",
    "    ret[n:] = ret[n:] - ret[:-n]\n",
    "    return ret[n - 1 :] / n\n",
    "\n",
    "\n",
    "def _compute_indices(breakpoints: dict, aqi: np.ndarray, pol: str, con: float) -> float:\n",
    "    \"\"\"Computes break points for pollutants\n",
    "\n",
    "    The compute_indices function first fetches the correct upper and lower bounds of AQI categories and breakpoint\n",
    "    concentrations for the input concentration and pollutant with the help of arrays AQI and breakpoints.\n",
    "\n",
    "    It is code copied straight from\n",
    "    https://github.com/numpy/numpy-tutorials/blob/main/content/tutorial-air-quality-analysis.md\n",
    "\n",
    "\n",
    "    :param breakpoints: dictionary of pollutant to break points.\n",
    "    :param aqi: array of AQI break points.\n",
    "    :param pol: pollutant\n",
    "    :param con: concentration\n",
    "    :return: index computed\n",
    "    \"\"\"\n",
    "    bp = breakpoints[pol]\n",
    "\n",
    "    if pol == \"CO\":\n",
    "        inc = 0.1\n",
    "    else:\n",
    "        inc = 1\n",
    "\n",
    "    if bp[0] <= con < bp[1]:\n",
    "        Bl = bp[0]\n",
    "        Bh = bp[1] - inc\n",
    "        Ih = aqi[1] - inc\n",
    "        Il = aqi[0]\n",
    "    elif bp[1] <= con < bp[2]:\n",
    "        Bl = bp[1]\n",
    "        Bh = bp[2] - inc\n",
    "        Ih = aqi[2] - inc\n",
    "        Il = aqi[1]\n",
    "    elif bp[2] <= con < bp[3]:\n",
    "        Bl = bp[2]\n",
    "        Bh = bp[3] - inc\n",
    "        Ih = aqi[3] - inc\n",
    "        Il = aqi[2]\n",
    "    elif bp[3] <= con < bp[4]:\n",
    "        Bl = bp[3]\n",
    "        Bh = bp[4] - inc\n",
    "        Ih = aqi[4] - inc\n",
    "        Il = aqi[3]\n",
    "    elif bp[4] <= con < bp[5]:\n",
    "        Bl = bp[4]\n",
    "        Bh = bp[5] - inc\n",
    "        Ih = aqi[5] - inc\n",
    "        Il = aqi[4]\n",
    "    elif bp[5] <= con:\n",
    "        Bl = bp[5]\n",
    "        Bh = bp[5] + bp[4] - (2 * inc)\n",
    "        Ih = aqi[6]\n",
    "        Il = aqi[5]\n",
    "    else:\n",
    "        print(f\"Concentration {con} out of range!\")\n",
    "        raise ValueError(f\"Concentration {con} out of range!\")\n",
    "\n",
    "    return ((Ih - Il) / (Bh - Bl)) * (con - Bl) + Il\n",
    "\n",
    "\n",
    "def _t_test(x, y):\n",
    "    \"\"\"Classic t-test\"\"\"\n",
    "    diff = y - x\n",
    "    var = np.var(diff, ddof=1)\n",
    "    num = np.mean(diff)\n",
    "    denom = np.sqrt(var / len(x))\n",
    "    return np.divide(num, denom)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 4 - Define your new Hamilton functions & curate them into a TemporaryFunctionModule object.\n",
    "# This enables you to add functions to your DAG without creating a proper module.\n",
    "# This is ONLY INTENDED FOR QUICK DEVELOPMENT. For moving to production move these to an actual module, or use `%%writefile`\n",
    "\n",
    "# Here I am separating out the function to calculate the AQI index array into a separate module\n",
    "\n",
    "\n",
    "def pollutant_data(input_file_name: str = \"air-quality-data.csv\") -> np.ndarray:\n",
    "    \"\"\"Returns the raw pollutant data.\"\"\"\n",
    "    return np.loadtxt(\n",
    "        input_file_name, dtype=float, delimiter=\",\", skiprows=1, usecols=range(1, 8)\n",
    "    )\n",
    "\n",
    "\n",
    "def pollutants_A(pollutant_data: np.ndarray) -> np.ndarray:\n",
    "    \"\"\"Pollutant A\"\"\"\n",
    "    return pollutant_data[:, 0:5]\n",
    "\n",
    "\n",
    "def pollutants_B(pollutant_data: np.ndarray) -> np.ndarray:\n",
    "    \"\"\"Pollutant B\"\"\"\n",
    "    return pollutant_data[:, 5:]\n",
    "\n",
    "\n",
    "def pollutants_A_24hr_avg(pollutants_A: np.ndarray) -> np.ndarray:\n",
    "    \"\"\"24 hour move average of pollutant A.\"\"\"\n",
    "    return _moving_mean(pollutants_A, 24)\n",
    "\n",
    "\n",
    "def pollutants_B_8hr_avg(\n",
    "    pollutants_B: np.ndarray, pollutants_A_24hr_avg: np.ndarray\n",
    ") -> np.ndarray:\n",
    "    \"\"\"8 hour move average of pollutant B.\n",
    "    To make sure both the sets are of the same length, we will truncate the pollutants_B_8hr_avg according to\n",
    "    the length of pollutants_A_24hr_avg. This will also ensure we have concentrations for all the pollutants\n",
    "    over the same period of time.\n",
    "    \"\"\"\n",
    "    return _moving_mean(pollutants_B, 8)[-(pollutants_A_24hr_avg.shape[0]) :]\n",
    "\n",
    "\n",
    "def pollutants(\n",
    "    pollutants_A_24hr_avg: np.ndarray, pollutants_B_8hr_avg: np.ndarray\n",
    ") -> np.ndarray:\n",
    "    \"\"\"Concatenates Pollutants A and Pollutants B.\n",
    "\n",
    "    Now, we can join both sets with np.concatenate to form a single data set of all the averaged concentrations.\n",
    "    Note that we have to join our arrays column-wise so we pass the axis=1 parameter.\n",
    "    \"\"\"\n",
    "    return np.concatenate((pollutants_A_24hr_avg, pollutants_B_8hr_avg), axis=1)\n",
    "\n",
    "\n",
    "def AQI() -> np.ndarray:\n",
    "    \"\"\"AQI ranges.\n",
    "    See https://github.com/numpy/numpy-tutorials/blob/main/content/tutorial-air-quality-analysis.md#calculating-the-air-quality-index\n",
    "    \"\"\"\n",
    "    return np.array([0, 51, 101, 201, 301, 401, 501])\n",
    "\n",
    "\n",
    "def breakpoints() -> dict:\n",
    "    \"\"\"AQI breakpoints for pollutants.\n",
    "    See https://github.com/numpy/numpy-tutorials/blob/main/content/tutorial-air-quality-analysis.md#calculating-the-air-quality-index\n",
    "    \"\"\"\n",
    "    return {\n",
    "        \"PM2.5\": np.array([0, 31, 61, 91, 121, 251]),\n",
    "        \"PM10\": np.array([0, 51, 101, 251, 351, 431]),\n",
    "        \"NO2\": np.array([0, 41, 81, 181, 281, 401]),\n",
    "        \"NH3\": np.array([0, 201, 401, 801, 1201, 1801]),\n",
    "        \"SO2\": np.array([0, 41, 81, 381, 801, 1601]),\n",
    "        \"CO\": np.array([0, 1.1, 2.1, 10.1, 17.1, 35]),\n",
    "        \"O3\": np.array([0, 51, 101, 169, 209, 749]),\n",
    "    }\n",
    "\n",
    "\n",
    "def sub_indices(\n",
    "    pollutants: np.ndarray, breakpoints: dict, AQI: np.ndarray\n",
    ") -> np.ndarray:\n",
    "    \"\"\"Return sub indicies.\n",
    "\n",
    "    The subindices for each pollutant are calculated according to the linear relationship between the AQI and standard\n",
    "    breakpoint ranges with the formula as above:\n",
    "\n",
    "        $$ Ip = \\dfrac{\\text{IHi – ILo}}{\\text{BPHi – BPLo}}\\cdot{\\text{Cp – BPLo}} + \\text{ILo} $$\n",
    "\n",
    "    By calling our vectorized function vcompute_indices for each pollutant, we get the sub-indices.\n",
    "    To get back an array with the original shape, we use np.stack.\n",
    "    \"\"\"\n",
    "    vcompute_indices = np.vectorize(partial(_compute_indices, breakpoints, AQI))\n",
    "    return np.stack(\n",
    "        (\n",
    "            vcompute_indices(\"PM2.5\", pollutants[..., 0]),\n",
    "            vcompute_indices(\"PM10\", pollutants[..., 1]),\n",
    "            vcompute_indices(\"NO2\", pollutants[..., 2]),\n",
    "            vcompute_indices(\"NH3\", pollutants[..., 3]),\n",
    "            vcompute_indices(\"SO2\", pollutants[..., 4]),\n",
    "            vcompute_indices(\"CO\", pollutants[..., 5]),\n",
    "            vcompute_indices(\"O3\", pollutants[..., 6]),\n",
    "        ),\n",
    "        axis=1,\n",
    "    )\n",
    "\n",
    "\n",
    "def aqi_array(sub_indices: np.ndarray) -> np.ndarray:\n",
    "    \"\"\"Using np.max, we find out the maximum sub-index for each period, which is our Air Quality Index!\"\"\"\n",
    "    return np.max(sub_indices, axis=1)\n",
    "\n",
    "\n",
    "# We'll place the hourly calculations to their own module\n",
    "hourly_calculations = ad_hoc_utils.create_temporary_module(\n",
    "    pollutant_data,\n",
    "    pollutants_A,\n",
    "    pollutants_B,\n",
    "    pollutants_A_24hr_avg,\n",
    "    pollutants_B_8hr_avg,\n",
    "    pollutants,\n",
    "    AQI,\n",
    "    breakpoints,\n",
    "    sub_indices,\n",
    "    aqi_array,\n",
    "    module_name=\"hourly_calculations\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cell 5 - Define your new Hamilton functions & curate them into a TemporaryFunctionModule object.\n",
    "# This enables you to add functions to your DAG without creating a proper module.\n",
    "# This is ONLY INTENDED FOR QUICK DEVELOPMENT. For moving to production move these to an actual module, or use `%%writefile`.\n",
    "\n",
    "# Finally we'll do the t_test and p_value calculations in thier own module\n",
    "\n",
    "\n",
    "def datetime_index(\n",
    "    pollutants_A_24hr_avg: np.ndarray, input_file_name: str\n",
    ") -> np.ndarray:\n",
    "    \"\"\"We will now import the datetime column from our original dataset into a datetime64 dtype array.\n",
    "    We will use this array to index the AQI array and obtain subsets of the dataset.\"\"\"\n",
    "    return np.loadtxt(\n",
    "        input_file_name, dtype=\"M8[h]\", delimiter=\",\", skiprows=1, usecols=(0,)\n",
    "    )[-(pollutants_A_24hr_avg.shape[0]) :]\n",
    "\n",
    "\n",
    "def after_lock(\n",
    "    aqi_array: np.ndarray, datetime_index: np.ndarray, after_lock_date: str\n",
    ") -> np.ndarray:\n",
    "    \"\"\"Grab period after lock down.\"\"\"\n",
    "    return aqi_array[np.where(datetime_index >= np.datetime64(after_lock_date))]\n",
    "\n",
    "\n",
    "def before_lock(\n",
    "    aqi_array: np.ndarray,\n",
    "    datetime_index: np.ndarray,\n",
    "    after_lock: np.ndarray,\n",
    "    before_lock_date: str,\n",
    ") -> np.ndarray:\n",
    "    \"\"\"Grab period before lock down.\"\"\"\n",
    "    return aqi_array[np.where(datetime_index <= np.datetime64(before_lock_date))][\n",
    "        -(after_lock.shape[0]) :\n",
    "    ]\n",
    "\n",
    "\n",
    "def rng() -> typing.Any:\n",
    "    \"\"\"Returns random generator (not sure what type it is).\"\"\"\n",
    "    return default_rng()\n",
    "\n",
    "\n",
    "def before_sample(rng: typing.Any, before_lock: np.ndarray) -> np.ndarray:\n",
    "    \"\"\"Returns sample before lock down.\"\"\"\n",
    "    return rng.choice(before_lock, size=30, replace=False)\n",
    "\n",
    "\n",
    "def after_sample(rng: typing.Any, after_lock: np.ndarray) -> np.ndarray:\n",
    "    \"\"\"Returns sample after lock down.\"\"\"\n",
    "    return rng.choice(after_lock, size=30, replace=False)\n",
    "\n",
    "\n",
    "def t_value(before_sample: np.ndarray, after_sample: np.ndarray) -> np.ndarray:\n",
    "    \"\"\"T-value computed from t-test between before and after sample.\"\"\"\n",
    "    return _t_test(before_sample, after_sample)\n",
    "\n",
    "\n",
    "def dof(before_sample: np.ndarray) -> int:\n",
    "    \"\"\"Degrees of freedom based on before sample.\"\"\"\n",
    "    return len(before_sample) - 1\n",
    "\n",
    "\n",
    "def p_value(t_value: np.ndarray, dof: int) -> float:\n",
    "    \"\"\"P-value computed from t-value and degrees of freedom.\"\"\"\n",
    "    return stats.distributions.t.cdf(t_value, dof)\n",
    "\n",
    "\n",
    "# We'll place the hourly calculations to their own module\n",
    "statistic_calculations = ad_hoc_utils.create_temporary_module(\n",
    "    datetime_index,\n",
    "    after_lock,\n",
    "    before_lock,\n",
    "    rng,\n",
    "    before_sample,\n",
    "    after_sample,\n",
    "    t_value,\n",
    "    dof,\n",
    "    p_value,\n",
    "    module_name=\"statistic_calculations\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:hamilton.telemetry:Note: Hamilton collects completely anonymous data about usage. This will help us improve Hamilton over time. See https://github.com/apache/hamilton#usage-analytics--data-privacy for details.\n"
     ]
    }
   ],
   "source": [
    "adapter = base.SimplePythonGraphAdapter(base.DictResult())\n",
    "# adapter = base.SimplePythonGraphAdapter(base.NumpyMatrixResult())  # could also get a numpy matrix back.\n",
    "dr = driver.Driver(\n",
    "    {\n",
    "        \"input_file_name\": \"air-quality-data.csv\",\n",
    "        \"after_lock_date\": \"2020-03-24T00\",\n",
    "        \"before_lock_date\": \"2020-03-21T00\",\n",
    "    },\n",
    "    hourly_calculations,\n",
    "    statistic_calculations,\n",
    "    adapter=adapter,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 2.43.0 (0)\n",
       " -->\n",
       "<!-- Title: %3 Pages: 1 -->\n",
       "<svg width=\"579pt\" height=\"908pt\"\n",
       " viewBox=\"0.00 0.00 579.31 908.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 904)\">\n",
       "<title>%3</title>\n",
       "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-904 575.31,-904 575.31,4 -4,4\"/>\n",
       "<!-- before_lock_date -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>before_lock_date</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" cx=\"118.94\" cy=\"-306\" rx=\"118.88\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"118.94\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\">Input: before_lock_date</text>\n",
       "</g>\n",
       "<!-- before_lock -->\n",
       "<g id=\"node20\" class=\"node\">\n",
       "<title>before_lock</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"270.94\" cy=\"-234\" rx=\"63.89\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"270.94\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">before_lock</text>\n",
       "</g>\n",
       "<!-- before_lock_date&#45;&gt;before_lock -->\n",
       "<g id=\"edge25\" class=\"edge\">\n",
       "<title>before_lock_date&#45;&gt;before_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M154.19,-288.76C176.8,-278.35 206.24,-264.8 229.9,-253.9\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"231.36,-257.08 238.98,-249.72 228.44,-250.72 231.36,-257.08\"/>\n",
       "</g>\n",
       "<!-- before_sample -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>before_sample</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"270.94\" cy=\"-162\" rx=\"77.99\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"270.94\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">before_sample</text>\n",
       "</g>\n",
       "<!-- t_value -->\n",
       "<g id=\"node8\" class=\"node\">\n",
       "<title>t_value</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"359.94\" cy=\"-90\" rx=\"44.39\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"359.94\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">t_value</text>\n",
       "</g>\n",
       "<!-- before_sample&#45;&gt;t_value -->\n",
       "<g id=\"edge7\" class=\"edge\">\n",
       "<title>before_sample&#45;&gt;t_value</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M292.03,-144.41C304.09,-134.93 319.36,-122.92 332.36,-112.69\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"334.82,-115.21 340.52,-106.27 330.5,-109.71 334.82,-115.21\"/>\n",
       "</g>\n",
       "<!-- dof -->\n",
       "<g id=\"node14\" class=\"node\">\n",
       "<title>dof</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"270.94\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"270.94\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">dof</text>\n",
       "</g>\n",
       "<!-- before_sample&#45;&gt;dof -->\n",
       "<g id=\"edge16\" class=\"edge\">\n",
       "<title>before_sample&#45;&gt;dof</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M270.94,-143.7C270.94,-135.98 270.94,-126.71 270.94,-118.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"274.44,-118.1 270.94,-108.1 267.44,-118.1 274.44,-118.1\"/>\n",
       "</g>\n",
       "<!-- rng -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>rng</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"381.94\" cy=\"-234\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"381.94\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">rng</text>\n",
       "</g>\n",
       "<!-- rng&#45;&gt;before_sample -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>rng&#45;&gt;before_sample</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M362.86,-220.97C347.02,-210.98 324.09,-196.52 305.1,-184.54\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"306.74,-181.44 296.42,-179.07 303.01,-187.36 306.74,-181.44\"/>\n",
       "</g>\n",
       "<!-- after_sample -->\n",
       "<g id=\"node5\" class=\"node\">\n",
       "<title>after_sample</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"437.94\" cy=\"-162\" rx=\"70.69\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"437.94\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">after_sample</text>\n",
       "</g>\n",
       "<!-- rng&#45;&gt;after_sample -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>rng&#45;&gt;after_sample</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M394.09,-217.81C401.03,-209.14 409.9,-198.05 417.84,-188.12\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"420.72,-190.12 424.24,-180.13 415.26,-185.75 420.72,-190.12\"/>\n",
       "</g>\n",
       "<!-- after_lock_date -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>after_lock_date</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" cx=\"443.94\" cy=\"-378\" rx=\"111.58\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"443.94\" y=\"-374.3\" font-family=\"Times,serif\" font-size=\"14.00\">Input: after_lock_date</text>\n",
       "</g>\n",
       "<!-- after_lock -->\n",
       "<g id=\"node17\" class=\"node\">\n",
       "<title>after_lock</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"437.94\" cy=\"-306\" rx=\"55.79\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"437.94\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\">after_lock</text>\n",
       "</g>\n",
       "<!-- after_lock_date&#45;&gt;after_lock -->\n",
       "<g id=\"edge20\" class=\"edge\">\n",
       "<title>after_lock_date&#45;&gt;after_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M442.46,-359.7C441.8,-351.98 441,-342.71 440.26,-334.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"443.75,-333.77 439.41,-324.1 436.77,-334.37 443.75,-333.77\"/>\n",
       "</g>\n",
       "<!-- after_sample&#45;&gt;t_value -->\n",
       "<g id=\"edge8\" class=\"edge\">\n",
       "<title>after_sample&#45;&gt;t_value</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M419.45,-144.41C409.23,-135.24 396.38,-123.7 385.24,-113.7\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"387.27,-110.82 377.49,-106.75 382.59,-116.03 387.27,-110.82\"/>\n",
       "</g>\n",
       "<!-- input_file_name -->\n",
       "<g id=\"node6\" class=\"node\">\n",
       "<title>input_file_name</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" cx=\"428.94\" cy=\"-882\" rx=\"114.28\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"428.94\" y=\"-878.3\" font-family=\"Times,serif\" font-size=\"14.00\">Input: input_file_name</text>\n",
       "</g>\n",
       "<!-- datetime_index -->\n",
       "<g id=\"node10\" class=\"node\">\n",
       "<title>datetime_index</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"480.94\" cy=\"-450\" rx=\"81.79\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"480.94\" y=\"-446.3\" font-family=\"Times,serif\" font-size=\"14.00\">datetime_index</text>\n",
       "</g>\n",
       "<!-- input_file_name&#45;&gt;datetime_index -->\n",
       "<g id=\"edge11\" class=\"edge\">\n",
       "<title>input_file_name&#45;&gt;datetime_index</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M444.21,-864.04C465.34,-838.74 500.94,-788.75 500.94,-739 500.94,-739 500.94,-739 500.94,-593 500.94,-552.82 492.99,-506.87 487.12,-478.39\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"490.49,-477.43 484.99,-468.38 483.64,-478.89 490.49,-477.43\"/>\n",
       "</g>\n",
       "<!-- pollutant_data -->\n",
       "<g id=\"node12\" class=\"node\">\n",
       "<title>pollutant_data</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"356.94\" cy=\"-810\" rx=\"77.19\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"356.94\" y=\"-806.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutant_data</text>\n",
       "</g>\n",
       "<!-- input_file_name&#45;&gt;pollutant_data -->\n",
       "<g id=\"edge14\" class=\"edge\">\n",
       "<title>input_file_name&#45;&gt;pollutant_data</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M411.51,-864.05C402.49,-855.28 391.33,-844.43 381.45,-834.83\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"383.82,-832.25 374.21,-827.79 378.94,-837.27 383.82,-832.25\"/>\n",
       "</g>\n",
       "<!-- p_value -->\n",
       "<g id=\"node7\" class=\"node\">\n",
       "<title>p_value</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"314.94\" cy=\"-18\" rx=\"46.29\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"314.94\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">p_value</text>\n",
       "</g>\n",
       "<!-- t_value&#45;&gt;p_value -->\n",
       "<g id=\"edge5\" class=\"edge\">\n",
       "<title>t_value&#45;&gt;p_value</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M349.28,-72.41C343.92,-64.08 337.31,-53.8 331.32,-44.49\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"334.19,-42.48 325.84,-35.96 328.3,-46.26 334.19,-42.48\"/>\n",
       "</g>\n",
       "<!-- aqi_array -->\n",
       "<g id=\"node9\" class=\"node\">\n",
       "<title>aqi_array</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"221.94\" cy=\"-378\" rx=\"53.89\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"221.94\" y=\"-374.3\" font-family=\"Times,serif\" font-size=\"14.00\">aqi_array</text>\n",
       "</g>\n",
       "<!-- aqi_array&#45;&gt;after_lock -->\n",
       "<g id=\"edge18\" class=\"edge\">\n",
       "<title>aqi_array&#45;&gt;after_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M259.56,-364.81C295.89,-353.03 351.03,-335.17 390.38,-322.41\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"391.59,-325.7 400.02,-319.29 389.43,-319.04 391.59,-325.7\"/>\n",
       "</g>\n",
       "<!-- aqi_array&#45;&gt;before_lock -->\n",
       "<g id=\"edge22\" class=\"edge\">\n",
       "<title>aqi_array&#45;&gt;before_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M230.83,-360.14C236,-349.89 242.38,-336.41 246.94,-324 254.46,-303.53 260.77,-279.64 265.03,-261.77\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"268.45,-262.54 267.3,-252.01 261.63,-260.96 268.45,-262.54\"/>\n",
       "</g>\n",
       "<!-- datetime_index&#45;&gt;after_lock -->\n",
       "<g id=\"edge19\" class=\"edge\">\n",
       "<title>datetime_index&#45;&gt;after_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M521.04,-434.06C537.75,-425.61 555.45,-413.21 564.94,-396 572.67,-381.99 573.81,-373.32 564.94,-360 549.79,-337.27 522.85,-324.27 497.66,-316.85\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"498.32,-313.4 487.76,-314.17 496.5,-320.16 498.32,-313.4\"/>\n",
       "</g>\n",
       "<!-- datetime_index&#45;&gt;before_lock -->\n",
       "<g id=\"edge23\" class=\"edge\">\n",
       "<title>datetime_index&#45;&gt;before_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M412.04,-440.2C381.19,-432.98 346.54,-419.86 322.94,-396 286.82,-359.47 275.78,-297.7 272.41,-262.23\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"275.89,-261.81 271.6,-252.13 268.91,-262.38 275.89,-261.81\"/>\n",
       "</g>\n",
       "<!-- pollutants -->\n",
       "<g id=\"node11\" class=\"node\">\n",
       "<title>pollutants</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"221.94\" cy=\"-522\" rx=\"57.69\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"221.94\" y=\"-518.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants</text>\n",
       "</g>\n",
       "<!-- sub_indices -->\n",
       "<g id=\"node22\" class=\"node\">\n",
       "<title>sub_indices</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"221.94\" cy=\"-450\" rx=\"64.19\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"221.94\" y=\"-446.3\" font-family=\"Times,serif\" font-size=\"14.00\">sub_indices</text>\n",
       "</g>\n",
       "<!-- pollutants&#45;&gt;sub_indices -->\n",
       "<g id=\"edge28\" class=\"edge\">\n",
       "<title>pollutants&#45;&gt;sub_indices</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M221.94,-503.7C221.94,-495.98 221.94,-486.71 221.94,-478.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"225.44,-478.1 221.94,-468.1 218.44,-478.1 225.44,-478.1\"/>\n",
       "</g>\n",
       "<!-- pollutants_A -->\n",
       "<g id=\"node13\" class=\"node\">\n",
       "<title>pollutants_A</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"356.94\" cy=\"-738\" rx=\"68.49\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"356.94\" y=\"-734.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants_A</text>\n",
       "</g>\n",
       "<!-- pollutant_data&#45;&gt;pollutants_A -->\n",
       "<g id=\"edge15\" class=\"edge\">\n",
       "<title>pollutant_data&#45;&gt;pollutants_A</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M356.94,-791.7C356.94,-783.98 356.94,-774.71 356.94,-766.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"360.44,-766.1 356.94,-756.1 353.44,-766.1 360.44,-766.1\"/>\n",
       "</g>\n",
       "<!-- pollutants_B -->\n",
       "<g id=\"node18\" class=\"node\">\n",
       "<title>pollutants_B</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"153.94\" cy=\"-666\" rx=\"68.79\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"153.94\" y=\"-662.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants_B</text>\n",
       "</g>\n",
       "<!-- pollutant_data&#45;&gt;pollutants_B -->\n",
       "<g id=\"edge21\" class=\"edge\">\n",
       "<title>pollutant_data&#45;&gt;pollutants_B</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M333.3,-792.84C318.04,-782.38 297.78,-768.44 279.94,-756 247.37,-733.29 210.27,-707.04 184.81,-688.96\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"186.82,-686.1 176.64,-683.16 182.77,-691.81 186.82,-686.1\"/>\n",
       "</g>\n",
       "<!-- pollutants_A_24hr_avg -->\n",
       "<g id=\"node15\" class=\"node\">\n",
       "<title>pollutants_A_24hr_avg</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"356.94\" cy=\"-666\" rx=\"115.88\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"356.94\" y=\"-662.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants_A_24hr_avg</text>\n",
       "</g>\n",
       "<!-- pollutants_A&#45;&gt;pollutants_A_24hr_avg -->\n",
       "<g id=\"edge17\" class=\"edge\">\n",
       "<title>pollutants_A&#45;&gt;pollutants_A_24hr_avg</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M356.94,-719.7C356.94,-711.98 356.94,-702.71 356.94,-694.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"360.44,-694.1 356.94,-684.1 353.44,-694.1 360.44,-694.1\"/>\n",
       "</g>\n",
       "<!-- dof&#45;&gt;p_value -->\n",
       "<g id=\"edge6\" class=\"edge\">\n",
       "<title>dof&#45;&gt;p_value</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M280.92,-73.12C286.23,-64.68 292.87,-54.1 298.88,-44.55\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"302.02,-46.13 304.38,-35.8 296.1,-42.4 302.02,-46.13\"/>\n",
       "</g>\n",
       "<!-- pollutants_A_24hr_avg&#45;&gt;datetime_index -->\n",
       "<g id=\"edge10\" class=\"edge\">\n",
       "<title>pollutants_A_24hr_avg&#45;&gt;datetime_index</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M368.91,-648.03C385.37,-624.37 415.49,-579.89 437.94,-540 449.51,-519.43 461.06,-495.28 469.27,-477.32\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"472.53,-478.61 473.47,-468.05 466.16,-475.72 472.53,-478.61\"/>\n",
       "</g>\n",
       "<!-- pollutants_A_24hr_avg&#45;&gt;pollutants -->\n",
       "<g id=\"edge12\" class=\"edge\">\n",
       "<title>pollutants_A_24hr_avg&#45;&gt;pollutants</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M357.63,-647.67C357.51,-628.18 354.45,-596.58 337.94,-576 322.67,-556.97 299.05,-544.5 277.1,-536.48\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"278.1,-533.12 267.5,-533.2 275.83,-539.74 278.1,-533.12\"/>\n",
       "</g>\n",
       "<!-- pollutants_B_8hr_avg -->\n",
       "<g id=\"node21\" class=\"node\">\n",
       "<title>pollutants_B_8hr_avg</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"218.94\" cy=\"-594\" rx=\"110.48\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"218.94\" y=\"-590.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants_B_8hr_avg</text>\n",
       "</g>\n",
       "<!-- pollutants_A_24hr_avg&#45;&gt;pollutants_B_8hr_avg -->\n",
       "<g id=\"edge27\" class=\"edge\">\n",
       "<title>pollutants_A_24hr_avg&#45;&gt;pollutants_B_8hr_avg</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M324.58,-648.59C305.38,-638.85 280.84,-626.4 260.24,-615.95\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"261.59,-612.71 251.09,-611.31 258.43,-618.95 261.59,-612.71\"/>\n",
       "</g>\n",
       "<!-- breakpoints -->\n",
       "<g id=\"node16\" class=\"node\">\n",
       "<title>breakpoints</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"362.94\" cy=\"-522\" rx=\"65.79\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"362.94\" y=\"-518.3\" font-family=\"Times,serif\" font-size=\"14.00\">breakpoints</text>\n",
       "</g>\n",
       "<!-- breakpoints&#45;&gt;sub_indices -->\n",
       "<g id=\"edge29\" class=\"edge\">\n",
       "<title>breakpoints&#45;&gt;sub_indices</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M332.69,-505.98C311.78,-495.6 283.77,-481.7 261.15,-470.47\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"262.65,-467.3 252.14,-465.99 259.54,-473.57 262.65,-467.3\"/>\n",
       "</g>\n",
       "<!-- after_lock&#45;&gt;after_sample -->\n",
       "<g id=\"edge4\" class=\"edge\">\n",
       "<title>after_lock&#45;&gt;after_sample</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M437.94,-287.87C437.94,-263.67 437.94,-219.21 437.94,-190.39\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"441.44,-190.19 437.94,-180.19 434.44,-190.19 441.44,-190.19\"/>\n",
       "</g>\n",
       "<!-- after_lock&#45;&gt;before_lock -->\n",
       "<g id=\"edge24\" class=\"edge\">\n",
       "<title>after_lock&#45;&gt;before_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M405.34,-291.34C379.42,-280.47 342.91,-265.17 314.48,-253.25\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"315.75,-249.99 305.18,-249.35 313.04,-256.44 315.75,-249.99\"/>\n",
       "</g>\n",
       "<!-- pollutants_B&#45;&gt;pollutants_B_8hr_avg -->\n",
       "<g id=\"edge26\" class=\"edge\">\n",
       "<title>pollutants_B&#45;&gt;pollutants_B_8hr_avg</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M169.34,-648.41C177.4,-639.73 187.42,-628.95 196.34,-619.34\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"198.95,-621.67 203.19,-611.96 193.83,-616.9 198.95,-621.67\"/>\n",
       "</g>\n",
       "<!-- AQI -->\n",
       "<g id=\"node19\" class=\"node\">\n",
       "<title>AQI</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"117.94\" cy=\"-522\" rx=\"27.9\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"117.94\" y=\"-518.3\" font-family=\"Times,serif\" font-size=\"14.00\">AQI</text>\n",
       "</g>\n",
       "<!-- AQI&#45;&gt;sub_indices -->\n",
       "<g id=\"edge30\" class=\"edge\">\n",
       "<title>AQI&#45;&gt;sub_indices</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M136.53,-508.49C151.26,-498.57 172.19,-484.49 189.64,-472.74\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"192.06,-475.33 198.4,-466.85 188.15,-469.53 192.06,-475.33\"/>\n",
       "</g>\n",
       "<!-- before_lock&#45;&gt;before_sample -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>before_lock&#45;&gt;before_sample</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M270.94,-215.7C270.94,-207.98 270.94,-198.71 270.94,-190.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"274.44,-190.1 270.94,-180.1 267.44,-190.1 274.44,-190.1\"/>\n",
       "</g>\n",
       "<!-- pollutants_B_8hr_avg&#45;&gt;pollutants -->\n",
       "<g id=\"edge13\" class=\"edge\">\n",
       "<title>pollutants_B_8hr_avg&#45;&gt;pollutants</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M219.68,-575.7C220.01,-567.98 220.41,-558.71 220.78,-550.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"224.27,-550.25 221.21,-540.1 217.28,-549.95 224.27,-550.25\"/>\n",
       "</g>\n",
       "<!-- sub_indices&#45;&gt;aqi_array -->\n",
       "<g id=\"edge9\" class=\"edge\">\n",
       "<title>sub_indices&#45;&gt;aqi_array</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M221.94,-431.7C221.94,-423.98 221.94,-414.71 221.94,-406.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"225.44,-406.1 221.94,-396.1 218.44,-406.1 225.44,-406.1\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.graphs.Digraph at 0x7fce380e2f10>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Visualize execution\n",
    "# To visualize do `pip install \"sf-hamilton[visualization]\"` if you want these to work\n",
    "\n",
    "# visualize all possible functions\n",
    "dr.display_all_functions() # no args needed for a notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 2.43.0 (0)\n",
       " -->\n",
       "<!-- Title: %3 Pages: 1 -->\n",
       "<svg width=\"600pt\" height=\"764pt\"\n",
       " viewBox=\"0.00 0.00 599.83 764.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 760)\">\n",
       "<title>%3</title>\n",
       "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-760 595.83,-760 595.83,4 -4,4\"/>\n",
       "<!-- before_sample -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>before_sample</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"337.89\" cy=\"-18\" rx=\"77.99\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"337.89\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">before_sample</text>\n",
       "</g>\n",
       "<!-- datetime_index -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>datetime_index</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"81.89\" cy=\"-306\" rx=\"81.79\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"81.89\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\">datetime_index</text>\n",
       "</g>\n",
       "<!-- after_lock -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>after_lock</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"241.89\" cy=\"-162\" rx=\"55.79\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"241.89\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">after_lock</text>\n",
       "</g>\n",
       "<!-- datetime_index&#45;&gt;after_lock -->\n",
       "<g id=\"edge8\" class=\"edge\">\n",
       "<title>datetime_index&#45;&gt;after_lock</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M85.44,-287.78C90.32,-268.11 100.85,-236.04 120.89,-216 139.41,-197.48 165.48,-184.94 188.69,-176.73\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"190.05,-179.96 198.42,-173.47 187.83,-173.32 190.05,-179.96\"/>\n",
       "</g>\n",
       "<!-- before_lock -->\n",
       "<g id=\"node7\" class=\"node\">\n",
       "<title>before_lock</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"283.89\" cy=\"-90\" rx=\"63.89\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"283.89\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">before_lock</text>\n",
       "</g>\n",
       "<!-- datetime_index&#45;&gt;before_lock -->\n",
       "<g id=\"edge13\" class=\"edge\">\n",
       "<title>datetime_index&#45;&gt;before_lock</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M80.99,-287.84C80.75,-269.07 82.59,-238.64 94.89,-216 124.52,-161.46 189.42,-126.67 234.86,-108\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"236.25,-111.21 244.24,-104.26 233.66,-104.71 236.25,-111.21\"/>\n",
       "</g>\n",
       "<!-- pollutants -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>pollutants</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"284.89\" cy=\"-378\" rx=\"57.69\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"284.89\" y=\"-374.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants</text>\n",
       "</g>\n",
       "<!-- sub_indices -->\n",
       "<g id=\"node10\" class=\"node\">\n",
       "<title>sub_indices</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"425.89\" cy=\"-306\" rx=\"64.19\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"425.89\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\">sub_indices</text>\n",
       "</g>\n",
       "<!-- pollutants&#45;&gt;sub_indices -->\n",
       "<g id=\"edge19\" class=\"edge\">\n",
       "<title>pollutants&#45;&gt;sub_indices</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M314.45,-362.33C335.32,-351.97 363.51,-337.97 386.31,-326.65\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"388,-329.72 395.4,-322.14 384.89,-323.45 388,-329.72\"/>\n",
       "</g>\n",
       "<!-- after_lock&#45;&gt;before_lock -->\n",
       "<g id=\"edge14\" class=\"edge\">\n",
       "<title>after_lock&#45;&gt;before_lock</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M252.06,-144.05C257.01,-135.8 263.07,-125.7 268.57,-116.54\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"271.68,-118.16 273.82,-107.79 265.67,-114.56 271.68,-118.16\"/>\n",
       "</g>\n",
       "<!-- pollutant_data -->\n",
       "<g id=\"node5\" class=\"node\">\n",
       "<title>pollutant_data</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"221.89\" cy=\"-666\" rx=\"77.19\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"221.89\" y=\"-662.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutant_data</text>\n",
       "</g>\n",
       "<!-- pollutants_B -->\n",
       "<g id=\"node6\" class=\"node\">\n",
       "<title>pollutants_B</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"424.89\" cy=\"-522\" rx=\"68.79\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"424.89\" y=\"-518.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants_B</text>\n",
       "</g>\n",
       "<!-- pollutant_data&#45;&gt;pollutants_B -->\n",
       "<g id=\"edge11\" class=\"edge\">\n",
       "<title>pollutant_data&#45;&gt;pollutants_B</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M245.53,-648.84C260.79,-638.38 281.05,-624.44 298.89,-612 331.46,-589.29 368.56,-563.04 394.02,-544.96\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"396.06,-547.81 402.19,-539.16 392.01,-542.1 396.06,-547.81\"/>\n",
       "</g>\n",
       "<!-- pollutants_A -->\n",
       "<g id=\"node9\" class=\"node\">\n",
       "<title>pollutants_A</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"221.89\" cy=\"-594\" rx=\"68.49\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"221.89\" y=\"-590.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants_A</text>\n",
       "</g>\n",
       "<!-- pollutant_data&#45;&gt;pollutants_A -->\n",
       "<g id=\"edge18\" class=\"edge\">\n",
       "<title>pollutant_data&#45;&gt;pollutants_A</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M221.89,-647.7C221.89,-639.98 221.89,-630.71 221.89,-622.11\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"225.39,-622.1 221.89,-612.1 218.39,-622.1 225.39,-622.1\"/>\n",
       "</g>\n",
       "<!-- pollutants_B_8hr_avg -->\n",
       "<g id=\"node8\" class=\"node\">\n",
       "<title>pollutants_B_8hr_avg</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"359.89\" cy=\"-450\" rx=\"110.48\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"359.89\" y=\"-446.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants_B_8hr_avg</text>\n",
       "</g>\n",
       "<!-- pollutants_B&#45;&gt;pollutants_B_8hr_avg -->\n",
       "<g id=\"edge16\" class=\"edge\">\n",
       "<title>pollutants_B&#45;&gt;pollutants_B_8hr_avg</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M409.49,-504.41C401.43,-495.73 391.41,-484.95 382.5,-475.34\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"385.01,-472.9 375.64,-467.96 379.88,-477.67 385.01,-472.9\"/>\n",
       "</g>\n",
       "<!-- before_lock&#45;&gt;before_sample -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>before_lock&#45;&gt;before_sample</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M296.96,-72.05C303.46,-63.63 311.45,-53.28 318.63,-43.97\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"321.6,-45.84 324.94,-35.79 316.06,-41.57 321.6,-45.84\"/>\n",
       "</g>\n",
       "<!-- pollutants_B_8hr_avg&#45;&gt;pollutants -->\n",
       "<g id=\"edge6\" class=\"edge\">\n",
       "<title>pollutants_B_8hr_avg&#45;&gt;pollutants</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M341.74,-432.05C332.2,-423.16 320.37,-412.11 309.97,-402.41\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"312.06,-399.57 302.36,-395.31 307.29,-404.69 312.06,-399.57\"/>\n",
       "</g>\n",
       "<!-- pollutants_A_24hr_avg -->\n",
       "<g id=\"node12\" class=\"node\">\n",
       "<title>pollutants_A_24hr_avg</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"221.89\" cy=\"-522\" rx=\"115.88\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"221.89\" y=\"-518.3\" font-family=\"Times,serif\" font-size=\"14.00\">pollutants_A_24hr_avg</text>\n",
       "</g>\n",
       "<!-- pollutants_A&#45;&gt;pollutants_A_24hr_avg -->\n",
       "<g id=\"edge23\" class=\"edge\">\n",
       "<title>pollutants_A&#45;&gt;pollutants_A_24hr_avg</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M221.89,-575.7C221.89,-567.98 221.89,-558.71 221.89,-550.11\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"225.39,-550.1 221.89,-540.1 218.39,-550.1 225.39,-550.1\"/>\n",
       "</g>\n",
       "<!-- aqi_array -->\n",
       "<g id=\"node11\" class=\"node\">\n",
       "<title>aqi_array</title>\n",
       "<ellipse fill=\"none\" stroke=\"red\" cx=\"425.89\" cy=\"-234\" rx=\"53.89\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"425.89\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">aqi_array</text>\n",
       "</g>\n",
       "<!-- sub_indices&#45;&gt;aqi_array -->\n",
       "<g id=\"edge22\" class=\"edge\">\n",
       "<title>sub_indices&#45;&gt;aqi_array</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M425.89,-287.7C425.89,-279.98 425.89,-270.71 425.89,-262.11\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"429.39,-262.1 425.89,-252.1 422.39,-262.1 429.39,-262.1\"/>\n",
       "</g>\n",
       "<!-- aqi_array&#45;&gt;after_lock -->\n",
       "<g id=\"edge7\" class=\"edge\">\n",
       "<title>aqi_array&#45;&gt;after_lock</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M391.71,-220C361.95,-208.67 318.61,-192.19 286.19,-179.85\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"287.1,-176.45 276.51,-176.17 284.61,-183 287.1,-176.45\"/>\n",
       "</g>\n",
       "<!-- aqi_array&#45;&gt;before_lock -->\n",
       "<g id=\"edge12\" class=\"edge\">\n",
       "<title>aqi_array&#45;&gt;before_lock</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M398.07,-218.45C381.47,-208.92 360.63,-195.4 344.89,-180 325.68,-161.2 308.84,-135.5 297.75,-116.56\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"300.78,-114.81 292.78,-107.86 294.7,-118.28 300.78,-114.81\"/>\n",
       "</g>\n",
       "<!-- pollutants_A_24hr_avg&#45;&gt;datetime_index -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>pollutants_A_24hr_avg&#45;&gt;datetime_index</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M210.67,-503.85C186.09,-466.27 127.36,-376.51 98.49,-332.37\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"101.38,-330.39 92.97,-323.94 95.52,-334.22 101.38,-330.39\"/>\n",
       "</g>\n",
       "<!-- pollutants_A_24hr_avg&#45;&gt;pollutants -->\n",
       "<g id=\"edge5\" class=\"edge\">\n",
       "<title>pollutants_A_24hr_avg&#45;&gt;pollutants</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M223.11,-503.81C224.97,-485.28 229.58,-455.31 240.89,-432 245.88,-421.73 253.26,-411.74 260.57,-403.26\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"263.36,-405.4 267.46,-395.63 258.16,-400.7 263.36,-405.4\"/>\n",
       "</g>\n",
       "<!-- pollutants_A_24hr_avg&#45;&gt;pollutants_B_8hr_avg -->\n",
       "<g id=\"edge17\" class=\"edge\">\n",
       "<title>pollutants_A_24hr_avg&#45;&gt;pollutants_B_8hr_avg</title>\n",
       "<path fill=\"none\" stroke=\"red\" d=\"M254.25,-504.59C273.45,-494.85 297.99,-482.4 318.59,-471.95\"/>\n",
       "<polygon fill=\"red\" stroke=\"red\" points=\"320.41,-474.95 327.74,-467.31 317.24,-468.71 320.41,-474.95\"/>\n",
       "</g>\n",
       "<!-- rng -->\n",
       "<g id=\"node13\" class=\"node\">\n",
       "<title>rng</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"392.89\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"392.89\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">rng</text>\n",
       "</g>\n",
       "<!-- rng&#45;&gt;before_sample -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>rng&#45;&gt;before_sample</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M380.96,-73.81C374.15,-65.14 365.43,-54.05 357.63,-44.12\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"360.28,-41.83 351.35,-36.13 354.78,-46.15 360.28,-41.83\"/>\n",
       "</g>\n",
       "<!-- input_file_name -->\n",
       "<g id=\"node14\" class=\"node\">\n",
       "<title>input_file_name</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" cx=\"149.89\" cy=\"-738\" rx=\"114.28\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"149.89\" y=\"-734.3\" font-family=\"Times,serif\" font-size=\"14.00\">Input: input_file_name</text>\n",
       "</g>\n",
       "<!-- input_file_name&#45;&gt;datetime_index -->\n",
       "<g id=\"edge4\" class=\"edge\">\n",
       "<title>input_file_name&#45;&gt;datetime_index</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M134.62,-720.04C113.49,-694.74 77.89,-644.75 77.89,-595 77.89,-595 77.89,-595 77.89,-449 77.89,-408.98 79.5,-362.64 80.68,-334.07\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"84.18,-334.18 81.1,-324.04 77.18,-333.89 84.18,-334.18\"/>\n",
       "</g>\n",
       "<!-- input_file_name&#45;&gt;pollutant_data -->\n",
       "<g id=\"edge10\" class=\"edge\">\n",
       "<title>input_file_name&#45;&gt;pollutant_data</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M167.32,-720.05C176.34,-711.28 187.51,-700.43 197.38,-690.83\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"199.89,-693.27 204.62,-683.79 195.01,-688.25 199.89,-693.27\"/>\n",
       "</g>\n",
       "<!-- after_lock_date -->\n",
       "<g id=\"node15\" class=\"node\">\n",
       "<title>after_lock_date</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" cx=\"241.89\" cy=\"-234\" rx=\"111.58\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"241.89\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">Input: after_lock_date</text>\n",
       "</g>\n",
       "<!-- after_lock_date&#45;&gt;after_lock -->\n",
       "<g id=\"edge9\" class=\"edge\">\n",
       "<title>after_lock_date&#45;&gt;after_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M241.89,-215.7C241.89,-207.98 241.89,-198.71 241.89,-190.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"245.39,-190.1 241.89,-180.1 238.39,-190.1 245.39,-190.1\"/>\n",
       "</g>\n",
       "<!-- before_lock_date -->\n",
       "<g id=\"node16\" class=\"node\">\n",
       "<title>before_lock_date</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" stroke-dasharray=\"5,2\" cx=\"472.89\" cy=\"-162\" rx=\"118.88\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"472.89\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">Input: before_lock_date</text>\n",
       "</g>\n",
       "<!-- before_lock_date&#45;&gt;before_lock -->\n",
       "<g id=\"edge15\" class=\"edge\">\n",
       "<title>before_lock_date&#45;&gt;before_lock</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M430.01,-145.12C400.33,-134.12 360.85,-119.5 330.41,-108.23\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"331.46,-104.89 320.87,-104.7 329.03,-111.45 331.46,-104.89\"/>\n",
       "</g>\n",
       "<!-- breakpoints -->\n",
       "<g id=\"node17\" class=\"node\">\n",
       "<title>breakpoints</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"425.89\" cy=\"-378\" rx=\"65.79\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"425.89\" y=\"-374.3\" font-family=\"Times,serif\" font-size=\"14.00\">breakpoints</text>\n",
       "</g>\n",
       "<!-- breakpoints&#45;&gt;sub_indices -->\n",
       "<g id=\"edge20\" class=\"edge\">\n",
       "<title>breakpoints&#45;&gt;sub_indices</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M425.89,-359.7C425.89,-351.98 425.89,-342.71 425.89,-334.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"429.39,-334.1 425.89,-324.1 422.39,-334.1 429.39,-334.1\"/>\n",
       "</g>\n",
       "<!-- AQI -->\n",
       "<g id=\"node18\" class=\"node\">\n",
       "<title>AQI</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"537.89\" cy=\"-378\" rx=\"27.9\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"537.89\" y=\"-374.3\" font-family=\"Times,serif\" font-size=\"14.00\">AQI</text>\n",
       "</g>\n",
       "<!-- AQI&#45;&gt;sub_indices -->\n",
       "<g id=\"edge21\" class=\"edge\">\n",
       "<title>AQI&#45;&gt;sub_indices</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M518.38,-364.81C502.19,-354.68 478.78,-340.05 459.56,-328.04\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"461.41,-325.07 451.07,-322.74 457.7,-331 461.41,-325.07\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.graphs.Digraph at 0x7fce39f30890>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# visualize the path of execution between two functions\n",
    "dr.visualize_path_between(\"pollutant_data\", \"before_sample\", strict_path_visualization=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'t_value': -6.608782880861736, 'p_value': 1.521472476564141e-07, 'before_sample': array([227.21793103, 317.43764535, 141.11576342, 428.21953782,\n",
      "       440.42577031, 124.52357383, 130.28223993, 158.08806208,\n",
      "       341.78729651, 119.72279362, 415.58298319, 372.26752907,\n",
      "       421.74369748, 476.3539916 , 127.84018456, 175.12340517,\n",
      "       308.52700581, 418.97654062, 327.56979651, 301.04262931,\n",
      "       113.48712248, 149.08919463, 343.8056686 , 175.09353448,\n",
      "       438.99264706, 291.84387931, 355.19002907, 293.15961207,\n",
      "       334.88383721, 253.47142241]), 'after_sample': array([ 92.46416667, 238.7124569 , 252.03791667,  99.0725    ,\n",
      "       110.77154362, 218.26668103, 165.6739094 , 106.44333893,\n",
      "       112.0865604 ,  61.14416667,  37.14305556, 164.60168624,\n",
      "       120.18706376,  87.37875   , 128.09322148,  88.90208333,\n",
      "       175.63405172,  75.39375   ,  96.27458333,  68.9075    ,\n",
      "       113.54083054,  99.67083333,  64.66      ,  67.93875   ,\n",
      "        93.58166667, 107.05129195, 119.16633389, 106.62245805,\n",
      "        66.97375   , 104.14856544])}\n",
      "The t value is -6.608782880861736 and the p value is 1.521472476564141e-07.\n"
     ]
    }
   ],
   "source": [
    "# let's create the dataframe!\n",
    "output = [\"t_value\", \"p_value\", \"before_sample\", \"after_sample\"]\n",
    "result = dr.execute(output)\n",
    "print(result)\n",
    "print(f\"The t value is {result['t_value']} and the p value is {result['p_value']}.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[227.21793103  92.46416667]\n",
      " [317.43764535 238.7124569 ]\n",
      " [141.11576342 252.03791667]\n",
      " [428.21953782  99.0725    ]\n",
      " [440.42577031 110.77154362]\n",
      " [124.52357383 218.26668103]\n",
      " [130.28223993 165.6739094 ]\n",
      " [158.08806208 106.44333893]\n",
      " [341.78729651 112.0865604 ]\n",
      " [119.72279362  61.14416667]\n",
      " [415.58298319  37.14305556]\n",
      " [372.26752907 164.60168624]\n",
      " [421.74369748 120.18706376]\n",
      " [476.3539916   87.37875   ]\n",
      " [127.84018456 128.09322148]\n",
      " [175.12340517  88.90208333]\n",
      " [308.52700581 175.63405172]\n",
      " [418.97654062  75.39375   ]\n",
      " [327.56979651  96.27458333]\n",
      " [301.04262931  68.9075    ]\n",
      " [113.48712248 113.54083054]\n",
      " [149.08919463  99.67083333]\n",
      " [343.8056686   64.66      ]\n",
      " [175.09353448  67.93875   ]\n",
      " [438.99264706  93.58166667]\n",
      " [291.84387931 107.05129195]\n",
      " [355.19002907 119.16633389]\n",
      " [293.15961207 106.62245805]\n",
      " [334.88383721  66.97375   ]\n",
      " [253.47142241 104.14856544]]\n"
     ]
    }
   ],
   "source": [
    "sample_matrix = base.NumpyMatrixResult().build_result(\n",
    "        before_sample=result[\"before_sample\"], after_sample=result[\"after_sample\"]\n",
    "    )\n",
    "print(sample_matrix)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
